use base64::engine::general_purpose;
use base64::alphabet::{STANDARD, URL_SAFE};
use base64::{DecodeError, Engine};


use sm3::{Digest, Sm3};
use sm3::digest::consts::U16;
use sm4::cipher::{BlockDecrypt, BlockEncrypt};
use sm4::Sm4;
use sm4::cipher::generic_array::GenericArray;

use sm4_gcm::Sm4Key;
use crypto_common::KeyInit;
use futures_util::StreamExt;
use num_cpus;
use tokio;
use tokio::fs::{File, OpenOptions};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter};
use tokio::sync::mpsc::{Receiver, Sender};

use crate::log_info;
use crate::ltools::ltime;

/* base64 byte 加密 */
pub fn base64_encode_byte_url_safe(bytes: &[u8]) -> String {
    let gp = general_purpose::GeneralPurpose::new(&URL_SAFE, general_purpose::NO_PAD);
    return gp.encode(bytes);
}
/* base64 str 加密 */
pub fn base64_encode_url_safe(bytes: &str) -> String {
    let gp = general_purpose::GeneralPurpose::new(&URL_SAFE, general_purpose::NO_PAD);
    return gp.encode(bytes);
}

/* base64 byte 解码 */
pub fn base64_decode_url_safe_byte(bytes: &str) -> Result<Vec<u8>, DecodeError> {
    let gp = general_purpose::GeneralPurpose::new(&URL_SAFE, general_purpose::NO_PAD);
    return gp.decode(bytes);
}

/* base64 str 解码 */
pub fn base64_decode_url_safe(data: &str) -> Result<String, DecodeError> {
    let gp = general_purpose::GeneralPurpose::new(&URL_SAFE, general_purpose::NO_PAD);
    let mut decode_bytes = gp.decode(data)?;
    return match std::str::from_utf8_mut(&mut decode_bytes) {
        Ok(d) => { Ok(d.to_string()) }
        Err(_e) => { Err(DecodeError::InvalidPadding) }
    };
}

/* base64 加密 standard模式 */
pub fn base64_encode_byte_standard(bytes: &[u8]) -> String {
    let gp = general_purpose::GeneralPurpose::new(&STANDARD, general_purpose::NO_PAD);
    return gp.encode(bytes);
}

/* base64 加密 standard模式 */
pub fn base64_encode_standard(bytes: &str) -> String {
    let gp = general_purpose::GeneralPurpose::new(&STANDARD, general_purpose::NO_PAD);
    return gp.encode(bytes);
}

/* base64 解码  standard模式 */
pub fn base64_decode_standard(data: &str) -> Result<String, DecodeError> {
    let gp = general_purpose::GeneralPurpose::new(&STANDARD, general_purpose::NO_PAD);
    let mut decode_bytes = gp.decode(data)?;
    return match std::str::from_utf8_mut(&mut decode_bytes) {
        Ok(d) => { Ok(d.to_string()) }
        Err(_e) => { Err(DecodeError::InvalidPadding) }
    };
}

/**
 * @Des:
 * @param:
 * @return:
 */
fn key_length16(key: &[u8]) -> Vec<u8> {
    // let len = key.len();
    // println!("key_length ： {:?}", key_vec);
    let mut key_vec = key.to_vec();
    key_vec.resize(BLOCK_SIZE, 0);
    return key_vec;
}

/* sm3 加密 */
pub fn sm3_encode(data: impl AsRef<[u8]>) -> Vec<u8> {
    // create a hasher object, to use it do not forget to import `Digest` trait
    let mut hasher = Sm3::new();

    // write input message
    hasher.update(data);

    // read hash digest and consume hasher
    let result = hasher.finalize();
    return result.iter().map(|byte| *byte).collect::<Vec<_>>();
}

pub fn sm3_encode_str(data: impl AsRef<[u8]>) -> String {
    return hex::encode(sm3_encode(data));
}


const BLOCK_SIZE: usize = 16;
/* 创建 sm4 */
fn create_sm4(key: &[u8]) -> Sm4 {
    let key_gen: &GenericArray<u8, U16> = GenericArray::from_slice(key);
    return Sm4::new(&key_gen);
}

/* padding 填充 */
fn padding_fill(encrypted: &mut Vec<u8>) {
    let length = encrypted.len();
    if length < 16 {
        let tc = BLOCK_SIZE - length;
        encrypted.resize(BLOCK_SIZE, tc as u8);
    }
}

/* 解析区分区块之后的总长度 */
fn jx_total_block_len(if_en_dc: bool, plaintext_len: usize) -> usize {
    if if_en_dc {
        return (plaintext_len + BLOCK_SIZE - 1) / BLOCK_SIZE - 1;
    } else {
        if plaintext_len % BLOCK_SIZE != 0 { return 0; }
        return plaintext_len / BLOCK_SIZE - 1;
    };
}

/* sm4 ECB pkcs7 填充 加密 */
pub fn sm4_encrypt_ebc(key: &[u8], plaintext: &[u8], if_padding: bool, if_encode: bool) -> Vec<u8> {
    let cipher = create_sm4(key);
    sm4_encrypt_ebc_in(&cipher, plaintext, if_padding, if_encode)
}

pub(crate) fn sm4_encrypt_ebc_in(cipher: &Sm4, plaintext: impl AsRef<[u8]>, if_padding: bool, if_encode: bool) -> Vec<u8> {
    // 计算总长度
    let plaintext_len = plaintext.as_ref().len();
    let total_len = jx_total_block_len(if_encode, plaintext_len);
    if total_len == 0 {
        return Vec::new();
    }
    let mut ciphertext = Vec::with_capacity(plaintext_len);

    for (i, chunk) in plaintext.as_ref().chunks(BLOCK_SIZE).enumerate() {
        // 加密
        let mut encrypted = chunk.to_vec();

        if if_encode {
            if i == total_len {
                if if_padding {
                    padding_fill(&mut encrypted);
                } else {
                    if encrypted.len() != BLOCK_SIZE {
                        break;
                    }
                }
            }

            let mut block = GenericArray::from_mut_slice(&mut encrypted);
            cipher.encrypt_block(&mut block);
            ciphertext.extend_from_slice(&block.to_vec());
        } else {
            let mut block = GenericArray::from_mut_slice(&mut encrypted);
            cipher.decrypt_block(&mut block);

            let mut result = block.to_vec();
            if i == total_len && if_padding {
                remove_pkcs(&mut result);
            }
            ciphertext.extend_from_slice(&result);
        }
    }
    return ciphertext;
}

pub async fn sm4_encrypt_ebc_file<F>(key: &[u8], file_addr: String, copy_addr: String, if_padding: bool, if_encode: bool, callback: F)
where
    F: FnOnce(u8, String) + std::marker::Send + 'static,
{
    log_info!(format!("开始加密 ： {:?}", ltime::get_system_time()));
    let cipher = create_sm4(key);

    tokio::spawn(async move {
        let (rx_read, tx_read) = tokio::sync::mpsc::channel(1024);
        let (rx_write, tx_write) = tokio::sync::mpsc::channel(1024);

        tokio::spawn(read_file_async(file_addr, rx_read));

        tokio::spawn(sm4_ebc_async(cipher, if_padding, if_encode, tx_read, rx_write));

        write_file_async(copy_addr, tx_write, callback).await;
    });
}

async fn sm4_ebc_async(cipher: Sm4, if_padding: bool, if_encode: bool, mut tx_read: Receiver<Vec<u8>>, rx: Sender<Vec<u8>>) {
    while let Some(data_u8) = tx_read.recv().await {
        if data_u8.len() == 0 {
            rx.send(data_u8.to_vec()).await.expect("发送消息失败");
            break;
        }

        let ciphertext = sm4_encrypt_ebc_in(&cipher, &data_u8, if_padding, if_encode);

        rx.send(ciphertext).await.expect("发送消息失败");
    }
}

/**
 * @Des: sm4 CBC 加密
 * @param:
 * @return:
 */
pub fn sm4_encrypt_cbc(key: &[u8], iv: &[u8], plaintext: &[u8], if_padding: bool, if_encode: bool) -> Vec<u8> {
    let cipher = create_sm4(key);
    let mut iv_byte = iv.to_vec();
    return sm4_encrypt_cbc_in(&cipher, &mut iv_byte, plaintext, if_padding, if_encode);
}

pub fn sm4_encrypt_cbc_in(cipher: &Sm4, iv_byte: &mut Vec<u8>, plaintext: impl AsRef<[u8]>, if_padding: bool, if_encode: bool) -> Vec<u8> {
    // 计算总长度
    let plaintext_len = plaintext.as_ref().len();

    let mut ciphertext = Vec::with_capacity(plaintext_len);
    // let mut iv_byte = iv.to_vec();


    let total_len = jx_total_block_len(if_encode, plaintext_len);
    if total_len == 0 {
        return ciphertext;
    }

    for (i, chunk) in plaintext.as_ref().chunks(BLOCK_SIZE).enumerate() {
        // 加密
        let mut encrypted = chunk.to_vec();

        if if_encode {
            if i == total_len {
                if if_padding {
                    padding_fill(&mut encrypted);
                } else {
                    if encrypted.len() != BLOCK_SIZE {
                        break;
                    }
                }
            }

            for i in 0..BLOCK_SIZE {
                encrypted[i] = iv_byte[i] ^ encrypted[i]
            }

            let mut block = GenericArray::from_mut_slice(&mut encrypted);
            cipher.encrypt_block(&mut block);
            ciphertext.extend_from_slice(&block.to_vec());
            iv_byte.copy_from_slice(&block);
        } else {
            let mut block = GenericArray::from_mut_slice(&mut encrypted);
            cipher.decrypt_block(&mut block);

            for j in 0..BLOCK_SIZE {
                block[j] = iv_byte[j] ^ block[j]
            }

            iv_byte.copy_from_slice(&chunk);

            let mut result = block.to_vec();
            if i == total_len && if_padding {
                remove_pkcs(&mut result);
            }
            ciphertext.extend_from_slice(&result);
        }
    }

    ciphertext
}

/* sm4 cbc 文件加密*/
pub async fn sm4_encrypt_cbc_file<F>(key: &[u8], iv: Vec<u8>, file_addr: String, copy_addr: String, if_padding: bool, if_encode: bool, callback: F)
where
    F: FnOnce(u8, String) + std::marker::Send + 'static,
{
    log_info!(format!("开始加密 ： {:?}", ltime::get_system_time()));
    let cipher = create_sm4(key);

    tokio::spawn(async move {
        let (rx_read, tx_read) = tokio::sync::mpsc::channel(1024);
        let (rx_write, tx_write) = tokio::sync::mpsc::channel(1024);

        tokio::spawn(read_file_async(file_addr, rx_read));

        tokio::spawn(sm4_cbc_async(cipher, iv, if_padding, if_encode, tx_read, rx_write));

        write_file_async(copy_addr, tx_write, callback).await;
    });
}

async fn sm4_cbc_async(cipher: Sm4, iv: Vec<u8>, if_padding: bool, if_encode: bool, mut tx_read: Receiver<Vec<u8>>, rx: Sender<Vec<u8>>) {
    let mut iv_vec = iv.clone();
    while let Some(data_u8) = tx_read.recv().await {
        if data_u8.len() == 0 {
            rx.send(data_u8.to_vec()).await.expect("发送消息失败");
            break;
        }
        let ciphertext = sm4_encrypt_cbc_in(&cipher, &mut iv_vec, &data_u8, if_padding, if_encode);
        rx.send(ciphertext).await.expect("发送消息失败");
    }
}

/* 异步读取数据 */
async fn read_file_async(file_addr: String, rx: Sender<Vec<u8>>) {
    let open_file = match tokio::fs::File::open(file_addr).await {
        Ok(t) => t,
        Err(_) => {
            return;
        }
    };

    let mut reader = BufReader::with_capacity(1024, open_file);
    loop {
        let mut lens = 0;
        {
            let buf = match reader.fill_buf().await {
                Ok(b) => b,
                Err(_) => {
                    rx.send(Vec::new()).await.expect("发送消息失败");
                    break;
                }
            };

            lens = buf.len();
            rx.send(buf.to_vec()).await.expect("发送消息失败");
            if buf.is_empty() {
                break;
            }
        }
        // 消耗已读取的数据
        reader.consume(lens);
    }
}

async fn write_file_async<F>(copy_addr: String, mut tx_write: Receiver<Vec<u8>>, callback: F)
where
    F: FnOnce(u8, String),
{
    let option_file = match OpenOptions::new()
        .write(true)
        .append(true)
        .create(true)
        .open(copy_addr).await {
        Ok(f) => f,
        Err(_) => {
            return;
        }
    };

    let mut writer: BufWriter<File> = BufWriter::new(option_file);

    while let Some(mut data_u8) = tx_write.recv().await {
        if data_u8.is_empty() {
            log_info!(format!("加载结束 ： {:?}", ltime::get_system_time()));
            callback(0, "加载成功".to_string());
            break;
        }

        writer.write_all(&data_u8).await.unwrap();
        writer.flush().await.unwrap();
    }
}

/* sm4 gcm 加密 */
pub fn sm4_gcm_encode(key: &[u8], plaintext: &[u8]) -> Vec<u8> {
    let sm4_key = Sm4Key::from_slice(key).unwrap();
    let ciphertext = sm4_gcm::sm4_gcm_encrypt(&sm4_key, &key, plaintext);
    ciphertext
}

pub fn sm4_gcm_encode_str(key: &[u8], plaintext: &[u8]) -> String {
    hex::encode(sm4_gcm_encode(key, plaintext))
}

pub fn sm4_gcm_decode(key: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, String> {
    let sm4_key = Sm4Key::from_slice(key).unwrap();
    let ciphertext = sm4_gcm::sm4_gcm_decrypt(&sm4_key, &key, plaintext);
    ciphertext
}

pub fn sm4_gcm_decode_str(key: &[u8], plaintext: &str) -> Result<String, String> {
    return match hex::decode(plaintext) {
        Ok(t) => {
            match sm4_gcm_decode(key, &t) {
                Ok(mut r) => {
                    match std::str::from_utf8_mut(&mut r) {
                        Ok(mut r) => {
                            Ok(r.to_string())
                        }
                        Err(_) => {
                            Err("字符串解析失败".to_string())
                        }
                    }
                }
                Err(_) => {
                    Err("sm4解析失败".to_string())
                }
            }
        }
        Err(_) => {
            Err("hex解析失败".to_string())
        }
    };
}

/* 移除 pkcs7 填充 */
fn remove_pkcs(text: &mut Vec<u8>) {
    let lenght = text.len();

    let last_num = *text.last().unwrap();
    if (last_num as usize) > lenght {
        return;
    }
    let end_index = lenght - (last_num as usize);

    let mut find = true;
    for item in &text[end_index..] {
        if *item != last_num {
            find = false;
        }
    }
    if find {
        text.truncate(end_index);
    }
}

// fn encrypt(data: &[u8], key: &[u8], nonce: &[u8; 12]) -> Result<Vec<u8>, aead::Unspecified> {
//     let unbound_key = aead::UnboundKey::new(&aead::AES_256_GCM, key)?;
//     // let less_safe_key = aead::LessSafeKey::new(unbound_key);
//     let mut encryptor = aead::SealingKey::new(unbound_key, &aead::AES_256_GCM);
//
//     let nonce = aead::Nonce::assume_unique_for_key(*nonce);
//     let mut ciphertext = Vec::with_capacity(encryptor.algorithm().max_overhead() + data.len());
//     encryptor.seal_in_place(&nonce, b"", data, &mut ciphertext)?;
//     Ok(ciphertext)
// }

pub fn test_ring() {
    let key = b"F1A5BEEF00112233";
    let iv = b"F1A5BEEFES112233";
    let content = "已是悬崖百丈冰，犹有花枝俏。俏也不争春，只把春来报。待到山花烂漫时，她在丛中笑。asdf";
    // sha256_hash("我像只鱼儿在你的河畔，是谁在你守候的茭白月光。柳下闻夜曲起舞轻红霓，我去你入狱踩断缝纫机。");
    // let result = base64_encode("我像只鱼儿在你的河畔，是谁在你守候的茭白月光。柳下闻夜曲起舞轻红霓，我去你入狱踩断缝纫机。");
    // println!("result: {}", result);
    // let result2 = base64_decode("5oiR5YOP5Y+q6bG85YS/5Zyo5L2g55qE5rKz55WU77yM5piv6LCB5Zyo5L2g5a6I5YCZ55qE6Iyt55m95pyI5YWJ44CC5p+z5LiL6Ze75aSc5puy6LW36Iie6L2757qi6ZyT77yM5oiR5Y675L2g5YWl54ux6Lip5pat57yd57qr5py644CC");
    // println!("result: {:?}", result2);

    // let data:[u8;30] = [230, 136, 145, 229, 142, 187, 228, 189, 160, 229, 133, 165, 231, 139, 177, 232, 184, 169, 230, 150, 173, 231, 188, 157, 231, 186, 171, 230, 156, 186];
    // println!("data: {}", std::str::from_utf8(&data).unwrap());

    // let b = "F1A5BEEF00112233".as_bytes();
    // println!("b: {:?}", b);
    // let mut content = "我去你入狱踩断缝纫机".as_bytes().to_vec();
    //
    // let result = sm4_encrypt_ebc(b, "我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机我去你入狱踩断缝纫机".as_bytes(), true, true);
    // sleep(Duration::from_millis(20));
    // println!("打印数据： {:?}", result);
    //
    // let b2 = "F1A5BEEF00112233".as_bytes();
    // let start_time = crate::ltools::ltime::get_system_time();
    // sm4_encrypt_ebc_file(b2, r"D:\it\a20.txt".to_string(), r"D:\it\a20_copy.txt".to_string(), true, true);
    // sm4_encrypt_ebc_file(b2, r"D:\it\a201.txt".to_string(), r"D:\it\a201_copy.txt".to_string(), true, true);
    //
    // println!("加密时长： {}", (crate::ltools::ltime::get_system_time() - start_time))
    log_info!("开始".to_string());
    let start_time = ltime::get_system_time();
    ////////////////////////////////// sm4 cbc 加解密 start /////////////////////////////
    {
        // let result = sm4_encrypt_cbc(key, iv, content.as_bytes(), true, true);
        // // log_info!(format!("结果是：{:?}",result));
        // // log_info!(format!("hex 值为：{:?}", hex::encode(result)));
        //
        // let mut result_jm = sm4_encrypt_cbc(key, iv, &result, true, false);
        // log_info!(format!("解密vec： {:?}", result_jm));
        // log_info!(format!("解密： {}", lstring::vec_string(&mut result_jm)));
    }
    ////////////////////////////////// sm4 cbc 加解密 end //////////////////////////////

    ////////////////////////////////// sm4 cbc 文件加解密 start /////////////////////////////
    {
        // sm4_encrypt_cbc_file(key, iv.to_vec(), r"D:\it\a20.txt".to_string(), r"D:\it\a20_copy.txt".to_string(), true, true);
        // log_info!("加密结束".to_string());

        sm4_encrypt_cbc_file(key, iv.to_vec(), r"D:\it\a20_copy.txt".to_string(), r"D:\it\a20_jm.txt".to_string(), true, false, |code, msg| {
            println!("code: {}, msg: {}", code, msg);
        });
        log_info!("加密结束".to_string());
        log_info!(format!("时长：{}",(ltime::get_system_time() - start_time)));
    }
    ////////////////////////////////// sm4 cbc 文件加解密 end //////////////////////////////
}




